/*
    a10_signal -

    Show gsignal basic usage.
    
*/
#include <glib.h>
#include <gio/gio.h>
#include <gobject/gobject.h>


#define MYFOO_TYPE    myfoo_get_type()
#define MYFOO_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST(klass, MYFOO_TYPE, MyFooClass))
#define IS_MYFOO_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE(klass, MYFOO_TYPE))
#define MYFOO(obj)    (G_TYPE_CHECK_INSTANCE_CAST(obj, MYFOO_TYPE, MyFoo))
#define IS_MYFOO(obj)   (G_TYPE_CHECK_INSTANCE_TYPE(obj, MYFOO_TYPE))
#define MYFOO_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS(obj, MYFOO_TYPE, MyFooClass))

typedef struct _MyFoo MyFoo;
typedef struct _MyFooClass MyFooClass;

struct _MyFoo {
    GObject parent;
};

struct _MyFooClass {
    GObjectClass parent_class;

    gchar* (*signal1_method)(MyFoo *foo);
    gchar* (*signal2_method)(MyFoo *foo);
    
};


G_DEFINE_TYPE(MyFoo, myfoo, G_TYPE_OBJECT)



//
static guint signal1_id;
static guint signal2_id;

//forward declaration
static gchar* signal1_method_handler(MyFoo* obj);
static gchar* signal2_method_handler(MyFoo* obj);
static gboolean connact_accumulator(GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data);

//required by G_DEFINE_TYPE
static void myfoo_class_init(MyFooClass *klass)
{
    g_print("myfoo_class_init()\n");
    klass->signal1_method = signal1_method_handler;
    klass->signal2_method = signal2_method_handler;

    //see gitlab, sdroege authored and pwithnall committed on 22 Dec 2020 
    //之前glib版本不允许method有返回值时仅设置G_SIGNAL_RUN_FIRST
    signal1_id = g_signal_new("signal1"
                        , MYFOO_TYPE
                        ,  G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST
                        , G_STRUCT_OFFSET(MyFooClass, signal1_method)
                        , connact_accumulator
                        , NULL
                        , NULL
                        , G_TYPE_STRING
                        , 0);

    g_assert(signal1_id != 0);
    
    signal2_id = g_signal_new("signal2"
                        , MYFOO_TYPE
                        , G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST
                        , G_STRUCT_OFFSET(MyFooClass, signal2_method)
                        , connact_accumulator
                        , NULL
                        , NULL
                        , G_TYPE_STRING
                        , 0);
    g_assert(signal1_id != 0);
}

static void myfoo_init(MyFoo* obj)
{
    g_print("myfoo_init()\n");
    //pass
}



static gchar* signal1_method_handler(MyFoo* obj)
{
    g_print("signal1_method_handler()\n");
    
    return g_strdup("signal1_method");
}

static gchar* signal2_method_handler(MyFoo* obj)
{
    g_print("signal2_method_handler()\n");


    return g_strdup("signal2_method");
}

static gboolean connact_accumulator(GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data)
{
    const gchar *sum = g_value_get_string(return_accu);
    const gchar *handler_ret = g_value_get_string(handler_return);

    g_print("connact_accumulator()\n");

    if(sum == NULL)
        g_value_set_string(return_accu, handler_ret);
    else
        g_value_take_string(return_accu, g_strconcat(sum, ", ", handler_ret, NULL));

    return TRUE;
}

static gchar* signal1_handler(MyFoo* obj, gpointer data)
{
    g_print("signal1_handler()\n");

    return g_strdup("signal1_handler");
}

static gchar* signal1_after_handler(MyFoo* obj, gpointer data)
{
    g_print("signal1_after_handler()\n");

    return g_strdup("signal1_after_handler");
}

static gchar* signal2_handler(MyFoo* obj, gpointer data)
{
    g_print("signal2_handler()\n");

    g_print("stop signal2 emission\n");
    g_signal_stop_emission_by_name(obj, "signal2");

    return g_strdup("signal2_handler");
}

static gchar* signal2_after_handler(MyFoo* obj, gpointer data)
{
    g_print("signal2_after_handler()\n");

    return g_strdup("signal2_after_handler");
}




int main(int argc, char* argv[])
{
    MyFoo *obj;
    gchar* ret;

    g_print("new signal\n");
    obj = g_object_new(MYFOO_TYPE, NULL);

    g_print("connect signal\n");
    g_signal_connect(obj, "signal1", G_CALLBACK(signal1_handler), NULL);
    g_signal_connect_after(obj, "signal1", G_CALLBACK(signal1_after_handler), NULL);
    g_signal_connect(obj, "signal2", G_CALLBACK(signal2_handler), NULL);
    g_signal_connect_after(obj, "signal2", G_CALLBACK(signal2_after_handler), NULL);

    g_print("emit signal\n");
    g_signal_emit_by_name(obj, "signal1", &ret);

    g_print("ret=%s\n", ret);
    g_free(ret);

    g_signal_emit_by_name(obj, "signal2", &ret);
    g_print("ret=%s\n", ret);
    g_free(ret);

    g_object_unref(obj);

    g_print("game over.\n");
    return 0;
}